package simplelab;

import java.awt.Point;
import java.util.ArrayList;
/*
 * The information about the tile's of the world is stored
 * in this class.
 */
//\chapter{saját to-do ezt ki kell venni a beadott anyagból}
//kajánál ha a másik felzabálja és ő azt nem látja akkor benne marad a food listájába a kaja evés közben is figyeljen
//utolsó helyen fordulás felesleges
//előre, körbe fordul maximális
//falakat belevegye a számolásba
//ha mégse venné fel a kaját az ellenfél, akkor visszamenjen
//doksi
//mackró cellák
//debug
//
public class World {

    public final static int worldWidth = 60;
    public final static int worldHeigth = 60;
    public final static int maxEatPerTurn = 200;
    public final static int foodLostPerTurn = 50;
    public final static int lowEnergy = 200;
    public final static int considerIntelAgeValid = 8;
    public final static int maxFoods = 4;
    public final static int stepCost = 5;
    public final static int turnCost = 2;
    public final static int foodlessTurnLimit = 30;
    public static int turn = -1; // The turn we are in, from our perception
    public Tile[][] tile;
    public int lastChangeTurn;
    public boolean[][] points = new boolean[worldWidth][worldHeigth];
    public int lastEnemyPoint;
    public static DebugMap debugMap;
    public boolean enemyOnFood;
    public ArrayList<Agent> Friendlies;
    public ArrayList<Agent> Hostiles;
    public boolean myTeamIDSet;
    public int myTeamID;
    public int leaderID;
    public int tasksGiven;
    public int teamEnergy;
    public static Point position;
    public int eating;
    public Point myTarget;
    public static ArrayList<Food> Foods;
    public Task mainTask;
    public int[] matesEnergy;
    public Tile[][] macroblock;
    public static int macro = 1;
    private int worldWidthMacro = (int) Math.ceil(worldWidth / (double) macro);
    private int worldHeigthMacro = (int) Math.ceil(worldHeigth / (double) macro);
    public boolean[][] macroPoints = new boolean[worldWidthMacro][worldHeigthMacro];
    Point[] mypos = new Point[deepness - 1];
    public boolean[][][] mytile = new boolean[deepness - 1][worldWidth][worldHeigth];
    public final static int deepness = 3;
    private boolean twoStep = true;
    private boolean guiOn = true;
    private int lastCombinate = 0; //az ujratervezést lehet késlelteti
    boolean aSearch = false;
    //private int myAgentCount = 2;
    static int nowAgentID = -1;
    private int lastTurnBefore = 1; // értéke minimum 1
    //public boolean macroblocks = true;
    //a terület kalkuláció gyorsítása érdekében
    private Point tempPos = new Point();
    private boolean dirx;

    public World() {
        matesEnergy = new int[10];
        for (int i = 0; i < 10; i++) {
            matesEnergy[i] = 0;
        }
        tile = new Tile[worldWidth][worldHeigth];
        for (int x = 0; x < worldWidth; x++) {
            for (int y = 0; y < worldHeigth; y++) {
                tile[x][y] = new Tile();
            }
        }
        macroblock = new Tile[worldWidthMacro][worldHeigthMacro];
        for (int x = 0; x < worldWidthMacro; x++) {
            for (int y = 0; y < worldHeigthMacro; y++) {
                macroblock[x][y] = new Tile();
            }
        }

        for (int k = 0; k < worldWidthMacro; k++) {
            for (int l = 0; l < worldHeigthMacro; l++) {
                macroPoints[k][l] = false;
            }
        }
        /*
        for (int k = 0; k < worldHeigth; k++) {
        for (int l = 0; l < worldWidth; l++) {
        points[k][l] = false;
        }
        }*/

        for (int i = 0; i < deepness - 1; i++) {
            mypos[i] = new Point();
        }
        lastChangeTurn = deepness;
        if (guiOn) {
            debugMap = new DebugMap(this);
        }
        Friendlies = new ArrayList<Agent>();
        Hostiles = new ArrayList<Agent>();
        Foods = new ArrayList<Food>();
        myTeamIDSet = false;
        myTeamID = -1;
        leaderID = 0;
        tasksGiven = 0;
        eating = 0;
        myTarget = new Point(-1, -1);
        lastEnemyPoint = 20000;
        enemyOnFood = false;
    }

    public int teamEnergies() {
        int temp = 0;
        for (int i = 0; i < 10; i++) {
            temp += matesEnergy[i];
        }
        return temp;
    }

    public static void error() {
        if (!Foods.isEmpty()) {
            int temp = min(position, nowAgentID);
            if (min(position, nowAgentID) % 1000 == 0) {
                Foods.remove(temp / 1000);
            }
        }

    }

    public static void errorBlock() { //TODO para lehet, mert lehet hogy nem lesz jó a nowagent, ha nem jó akkor az adott hiba osztályban kell babrálni
        if (!Foods.isEmpty()) {
            int temp = min(position, nowAgentID);
            // if(manhattan(pos, Foods.get(temp)) ==1){
            if(temp >= 1000){
                Foods.remove(temp / 1000);
            }
            //}
        }

    }

    private boolean notInsideFood(Food food) {
        int size = Foods.size();
        for (int i = 0; i < size; i++) {
            if (Foods.get(i).position.x == food.position.x && Foods.get(i).position.y == food.position.y) {
                return false;
            }
        }

        return true;
    }

    public void foodManages(Beliefs beliefs) {
        int size = beliefs.foods.size();
        for (int i = 0; i < size; i++) {
            if (notInsideFood(beliefs.foods.get(i))) {
                Foods.add(beliefs.foods.get(i));
            }
        }
    }

    //visszaadja a legk�zelebbi kaja sorsz�m�t
    //ha a rajta �ll, akkor a sorsz�mmalával*1000 t�r vissza
    //egyégként pedig a távolság és a sorszám*1000-rel
    //ha az ID != 0 akkor csak azokat a kajákat adja vissza amelyeknek nincsen gazdája
   // -1-el tér vissza ha nincs használható food
    public static int min(Point pos, int ID) {
        int temp = 0;
        int min = worldHeigth * worldWidth ;
        for (int i = 1; i < Foods.size(); i++) {
            if (manhattan(pos, Foods.get(i).position) < min &&  (Foods.get(i).ownerID == -1 || Foods.get(i).ownerID == nowAgentID)) {
                temp = i;
                min = manhattan(pos, Foods.get(i).position);
            }
        }
        if (min == 0) {

            return temp * 1000;
        }else if(min == worldHeigth * worldWidth ){
            return -1;
        }
        return (min + (temp * 1000));
    }

    public Command Manage(Beliefs beliefs) {
        //TODO lehet hogy kelleni fog az hogy az ID az első ágensnek meg legyen jegyezve és amikor az az id jön akkor újratervezni a kaját
        MyTeamID(beliefs.team);

        nowAgentID = beliefs.id;
        refreshAgents(beliefs.pos, beliefs.agents);

        position = beliefs.pos;
        lastChangeTurn++;

        //a frissítés (refreshTile) után 4 körig még megengedi a hátra forgást, ez a változó ezt szabályozza
        ///  System.out.println(Math.ceil(worldWidth /(double)macro)+"sdf " +(worldWidth / macro)+ "  fs "+ (worldWidth / (double) macro) );
        eating--;
        //turn = beliefs.

        if (beliefs.energy < lowEnergy) {
            return new Command(Command.Action.Wait, 0, 0);
        }
        matesEnergy[beliefs.id] = beliefs.energy;
        teamEnergy = teamEnergies();
        int enemyPoint = (int) (teamEnergy / beliefs.ratio) - teamEnergy;
        ///System.out.println(enemyPoint + " asd "+ lastEnemyPoint + " akdsjfa "+ teamEnergy+ " ratio "+beliefs.ratio);
        //átállítja ha az ellenség kajálni kezd
        if (enemyPoint > lastEnemyPoint + maxEatPerTurn / 4) {
            enemyOnFood = true;
        } else {
            //ha befejezte az ellenség a kajálást, frissít
            if (enemyOnFood) {
                refreshTiles();
            }
            enemyOnFood = false;
        }

        lastEnemyPoint = enemyPoint;
        //ha maxfood+1 akkor kaj�l
        int temp= -1;
        if (beliefs.foods.size() > 0) {
            foodManages(beliefs);
            temp = min(beliefs.pos, nowAgentID);
            
            for(int i =0; i < Foods.size();i ++){
                System.out.println(Foods.get(i).ownerID + "   " + Foods.get(i).position  + " now: "+  nowAgentID);
                if(nowAgentID == Foods.get(i).ownerID){
                    
                }
            }
            System.out.println(temp + " sdf " + nowAgentID);
            if (temp % 1000 == 0) {
                int real = temp / 1000;
                Foods.get(real).value -= maxEatPerTurn + foodLostPerTurn;

                //kit�rli ha a k�r v�gi �tkez�s ut�n a kaja �rt�ke 1 al� cs�kkent

                if (Foods.get(real).value < 1) {
                    Foods.remove(real);
                    refreshTiles();
                }
                return new Command(Command.Action.Eat, 0, 0);
            }


        }
        //megkeresi a k�vetkez� kaj�t
        if (temp >= 0) {
            mainTask = findTarget(beliefs.pos, beliefs.agents, beliefs.dir);
        } else {
            //ha az utolsó körben sikerült lefutnia egy combinatenek és még van pár lépés benne akkor nem változtatja
            if (lastChangeTurn > lastCombinate) {
                //OneTile(beliefs.pos, beliefs.dir);
                mainTask = Combinate(beliefs.pos, beliefs.dir);
                lastChangeTurn = 0;
            }
        }


        if (!mainTask.task.isEmpty()) {

            beliefs.dir = makeItNull(beliefs.pos, beliefs.dir, mainTask);
            if (guiOn) {
                DebugMap.mcanvas.paint(DebugMap.mcanvas.getGraphics(), beliefs.pos, beliefs.dir, Foods);
            }
            int mainTaskX = mainTask.task.get(0).x;
            int mainTaskY = mainTask.task.get(0).y;
            mainTask.task.remove(0);
            if (mainTaskX == 0) {
                return new Command(Command.Action.Step, mainTaskY, 0);
            } else if (mainTaskX == 1) {
                return new Command(Command.Action.Turn, mainTaskY, 0);
            } else {
                return new Command(Command.Action.Eat, mainTaskY, 0);
            }
        } else {
            return new Command(Command.Action.Step, 0, 0);
        }

    }
    //kikeresi a legk�zelebbi kaj�t

    private Task findTarget(Point pos, ArrayList<Agent> agents, int dir) {
        System.out.println(Foods);
        int onFood = min(pos, nowAgentID);
        int temp = onFood / 1000;
        if (onFood % 1000 == 0) {

            Task ret = new Task();
            Foods.get(temp).value -= maxEatPerTurn + foodLostPerTurn;
            if (Foods.get(temp).value < 1) {
                Foods.remove(temp);
                refreshTiles();
            }

            ret.task.add(new Point(3, 0));
            return ret;
        }
        //ha nincs elérhető kaja akkor újratervezés
        if(onFood == -1){
            return Combinate(pos, dir);
        }
        return findPath(pos, temp, agents, dir);
    }
    //meghat�rozza az utat a kajáig
    private Task findPath(Point pos,int seq , ArrayList<Agent> agents, int dir) {
        Task ret = new Task();
        System.out.println(ret + " asdlm " + seq);
        Food closest = Foods.get(seq);    
        if (aSearch) {
            int dirx = -1, diry = -1;
            if (closest.position.x > pos.x) {
                dirx = 1;
            } else if (closest.position.x < pos.x) {
                dirx = 3;
            } else if (closest.position.y > pos.y) {
                diry = 2;
            } else if (closest.position.y < pos.y) {
                diry = 0;
            }


        }
        Point enemyPos = null;
        if (agents != null && agents.size() > 0) { // 
            int n = 0;
            for (int i = 0; i < agents.size(); i++) {
                //if (agents.get(i).team != myTeamID) { // TODO megcsinálni, az összes ellenséges ágensre
                if (agents.get(i).position.equals(pos) ) {
                    enemyPos = agents.get(i).position;
                    n = i;
                }
                //System.out.println(" itt van " + enemy.size());
                //ha van ellenséges ágens a láthatáron akkor megnézzük hogy mi vagyunk-e közelebb a kajához
                //ha igen akkor oda megyünk, egyébként tovább felderítünk
                if (enemyPos != null) {
                    if (min(enemyPos, nowAgentID) % 1000 == 0) {
                        for (int l = 0; l < Foods.size(); l++) {
                            if (enemyPos.x == Foods.get(l).position.x && enemyPos.y == Foods.get(l).position.y) {
                                Foods.remove(l);
                                if (Foods.size() > 0) {
                                    return findTarget(pos, agents, dir);
                                } else {
                                    return Combinate(pos, dir);
                                }
                            }
                        }
                        //törli ha az ellenség közelebb van a gép
                    } else if (manhattan(enemyPos, closest.position) < manhattan(pos, closest.position) && agents.get(n).IsFacing(closest.position)) {
                        // System.out.println(" törlés előtt "+Foods.size());
                        Foods.remove(closest);
                        // System.out.println(" törlés utn "+Foods.size());
                        if (Foods.size() > 0) {
                            return findTarget(pos, agents, dir);
                        } else {
                            return Combinate(pos, dir);
                        }
                    }
                }
                enemyPos = null;
            }
        }
        System.out.println(Foods.get(seq).ownerID + " awdf "+ nowAgentID);
        Foods.get(seq).ownerID = nowAgentID;
        Point tempPoint = null;
        if (closest.position.x > pos.x) {
            tempPoint = new Point(0, 1);
        } else if (closest.position.x < pos.x) {
            tempPoint = new Point(0, 3);
        } else if (closest.position.y > pos.y) {
            tempPoint = new Point(0, 2);
        } else if (closest.position.y < pos.y) {
            tempPoint = new Point(0, 0);
        }

        if (tempPoint.y != dir) {
            ret.task.add(new Point(1, tempPoint.y));
        } else {
            ret.task.add(tempPoint);
        }


        return ret;
    }

    public static int manhattan(Point first, Point second) {
        return Math.abs(first.x - second.x) + Math.abs(first.y - second.y);

    }

    public Task CalculateMacro(Point pos, int dir, int[] actions, int turnCount) {
        int plus;

        Task returner = new Task();
        //megmondja hogy x tengelyre párhuzamos-e a dir
        tempPos.x = pos.x / macro;
        tempPos.y = pos.y / macro;
        //segéd változók a az adott pont kiszámításához
        int tempX = 0, tempY = 0;
        for (int k = 0; k < deepness; k++) {
            /**
             * actions: 
             * 0 move forward
             * 1 turn right
             * 2 turn back
             * 3 turn left
             */
            if (actions[k] != 0) {
                dir = (dir + actions[k]) % 4;

                returner.task.add(new Point(1, dir));

            }
            //how the pos will change
            if (dir == 2 || dir == 1) {
                plus = 1;
            } else {
                plus = -1;
            }

            dirx = (dir % 2 == 0);

            //pos moving
            if (actions[k] == 0) {
                if (dirx) {
                    tempPos.y += plus;
                } else {
                    tempPos.x += plus;
                }
                returner.task.add(new Point(0, dir));
            }

            //put the title
            for (int i = 1; i < 10 / macro + 1; ++i) {
                if (dirx) {
                    tempY = tempPos.y + plus * i;
                } else {
                    tempX = tempPos.x + plus * i;
                }
                for (int l = -1 * i; l <= i; ++l) {
                    if (dirx) {
                        tempX = tempPos.x + l;
                    } else {
                        tempY = tempPos.y + l;
                    }
                    if (tempX > -1 && tempX < worldWidthMacro && tempY > -1 && tempY < worldHeigthMacro) {
                        macroPoints[tempX][tempY] = true;

                    }
                    //ha kifele néz else if()
                }
            }

        }
        //bool t�mb, 
        for (int i = 0; i < worldWidthMacro; i++) {
            for (int l = 0; l < worldHeigthMacro; l++) {
                ///System.out.println(l + " l "+ i + " w "+ worldHeigth / macro);
                if (macroPoints[i][l]) {
                    returner.profit += macroblock[i][l].value;
                    macroPoints[i][l] = false;
                }
            }
        }
        returner.profit = returner.profit * 10 / ((deepness - turnCount) * stepCost + turnCount * turnCost);
        return returner;




    }

    private boolean inSide(int[] tempDeep, Point pos, int dir) {
        int plus;
        int i = 0;
        int tempX = pos.x, tempY = pos.y;
        do {
            if (tempDeep[i] != 0) {
                dir = (dir + tempDeep[i]) % 4;
            }
            //how the pos will change
            if (dir == 2 || dir == 1) {
                plus = 1;
            } else {
                plus = -1;
            }
            dirx = dir % 2 == 0;
            //pos moving
            if (tempDeep[i] == 0) {
                if (dirx) {
                    tempY += plus;
                } else {
                    tempX += plus;
                }
            }
        } while (i++ < tempDeep.length - 1 && !((tempY > worldHeigth - 10 && dir == 2)
                || (tempY < 10 && dir == 0) || (tempX > worldWidth - 10 && dir == 1)
                || (tempX < 10 && dir == 3)));
        if (i == tempDeep.length) {
            return true;
        }
        return false;
    }

    public Task ReCombinateMacro(int deep, Point pos, int dir, int[] temp) {
        Task tempTask, maxProfit = new Task();




        int z = 1;
        //a kaja utáni első három körben a hátrafordulás is engedélyezett
        /*if (eating > 0) {
        z = 1;
        }*/
        int[] tempDeep = new int[deepness];
        for (int i = 0; i < deepness; ++i) {
            tempDeep[i] = 0;
        }
        maxProfit = CalculateMacro(pos, dir, tempDeep, 0);
        for (int i = deepness - lastTurnBefore; i > -1; --i) {
            for (int n = 1; n < 4; n += z) {
                tempDeep[i] = n;
                if (inSide(tempDeep, pos, dir)) {
                    tempTask = CalculateMacro(pos, dir, tempDeep, 1);
                    if (tempTask.profit > maxProfit.profit) {
                        maxProfit = tempTask;
                    }
                }
            }
            tempDeep[i] = 0;
        }
        if (twoStep) {
            for (int i = deepness - lastTurnBefore; i > -1; --i) {

                for (int n = 1; n < 4; n += z) {
                    tempDeep[i] = n;
                    for (int l = deepness - 1; l > i; --l) {
                        for (int j = 1; j < 4; j += 2) {
                            tempDeep[l] = j;
                            if (inSide(tempDeep, pos, dir)) {
                                tempTask = CalculateMacro(pos, dir, tempDeep, 2);
                                if (tempTask.profit > maxProfit.profit) {
                                    maxProfit = tempTask;
                                }
                            }
                        }
                        tempDeep[l] = 0;
                    }

                }
                tempDeep[i] = 0;
            }


        }

        //}
        return maxProfit;
    }

    public Task Combinate(Point pos, int dir) {
        Task maxProfit;
        maxProfit = new Task();

        maxProfit = ReCombinateMacro(0, pos, dir, null);
        if (maxProfit.profit == 0) {
            for (int i = 0; i < worldWidthMacro; i++) {
                for (int l = 0; l < worldHeigthMacro; l++) {
                    macroblock[i][l].value = 4;
                }
            }
            maxProfit = ReCombinateMacro(0, pos, dir, null);
        }
        //}
        return maxProfit;
    }

    //kinull�zza a l�tott mez�k �rt�k�t
    //átállítja a beliefseket, az akció utánira, azaz lépteti vagy elforgatja a nézetet
    //debugonhoz kell ez
    public int makeItNull(Point pos, int dir, Task maxProfit) {

        int plus;
        int maxProfitX = maxProfit.task.get(0).x,
                maxProfitY = maxProfit.task.get(0).y;


        // turning x = 1, y =  the dir
        if (maxProfitX == 1) {
            dir = maxProfitY;
        }
        //how the pos will change
        if (dir == 2 || dir == 1) {
            plus = 1;
        } else {
            plus = -1;
        }




        //lépésnél változik a pozíció
        if (maxProfitX == 0) {
            if (maxProfitY % 2 == 0) {
                pos.y += plus;
            } else {
                pos.x += plus;
            }
        }

        int tempX = (pos.x) / macro, tempY = (pos.y) / macro;
        ///System.out.println(tempX+ "    dsfd   " + tempY);
        macroblock[tempX][tempY].value = 0;
        for (int i = 1; i < 10 / macro + 1; ++i) {
            for (int l = -1 * i; l <= i; ++l) {
                if (dir % 2 == 0) {
                    tempX = pos.x / macro + l;
                    tempY = pos.y / macro + plus * i;
                } else {
                    tempX = pos.x / macro + plus * i;
                    tempY = pos.y / macro + l;
                }
                /// System.out.println(tempX+ "po" + tempY + "asdfdsa"+ pos.x+ " "+pos.y +" " + plus);
                if (tempX > -1 && tempX < worldWidthMacro && tempY > -1 && tempY < worldHeigthMacro) {
                    macroblock[tempX][tempY].value = 0;
                }

            }

        }
        return dir;
    }

    private void refreshTiles() {
        lastChangeTurn = deepness;
        eating = 4;

        for (int i = 0; i < worldWidthMacro; i++) {
            for (int l = 0; l < worldHeigthMacro; l++) {
                if (macroblock[i][l].value < 4) {
                    macroblock[i][l].value++;
                }
            }
        }

    }

    public void MyTeamID(int myTeamID) {
        if (myTeamIDSet == false) {
            this.myTeamID = myTeamID;
            myTeamIDSet = true;
        }
    }

    public void ManageHostileAgent(Agent agent) {
        // Is it really hostile?
        if (agent.team != myTeamID) {
            // Is agent on list?
            int i;
            for (i = 0; i < Hostiles.size() && Hostiles.get(i).id != agent.id; i++);
            if (i == Hostiles.size()) {
                Hostiles.add(agent);
            } else {
                Hostiles.get(i).RefreshIntel(agent);
            }
        }
    }

    //aktualizálja a friendliesben a pozícióját és a látott ellenséges ágensek pozicóját
    private void refreshAgents(Point pos, ArrayList<Agent> agents) {
        for(int i=0; i < agents.size(); i++){
            ManageHostileAgent(agents.get(i));
        }
        for(int i =0; i< Friendlies.size(); i++){
            if(Friendlies.get(i).id == nowAgentID){
                Friendlies.set(i,Friendlies.get(i).setPos(pos)); //TODO nem csak a pozíciót beállítani
            }
        }
    }
}
